// WindowsProject.cpp: 定义应用程序的入口点。
//

#include "stdafx.h"
#include "WindowsProject.h"

#define MAX_LOADSTRING 100

// 全局变量: 
HINSTANCE hInst;                                // 当前实例
WCHAR szTitle[MAX_LOADSTRING];                  // 标题栏文本
WCHAR szWindowClass[MAX_LOADSTRING];            // 主窗口类名

// 此代码模块中包含的函数的前向声明: 

/*
	ATOM 表示 unsigned short ， 
	MyRegisterClass 函数用于注册该窗口应用程序
	HINSTANCE hInstance 窗口句柄
*/
ATOM                MyRegisterClass(HINSTANCE hInstance);

/*
	BOOL 表示 int
	InitInstance是一个虚函数，作用：初始化
*/
BOOL                InitInstance(HINSTANCE, int);

/*
	WndProc是一个窗口回调函数

	参数：
		窗口句柄(Window Handle) HWND,
		消息ID(Message ID) UINT,
		两个消息参数(wParam, lParam)WPARAM、LPARAM,
	返回值：long

	WndProc的第一个参数hWnd就是当前接收消息的窗口句柄，第二个参数就是被传送过来的消息，第三、第四个参数都是附加在消息上的数据
*/
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
/*
	"关于"对话框的回调函数
	返回值：int
*/
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);

/*
	win32程序入口函数
		第一个参数被称作「执行实体句柄」
		第二个参数总是NULL
		第三个参数是用于执行程序的命令列
		第四个参数指出程序最初显示的方式，可以是正常的或者是最大化地充满整个画面，或者是最小化显示在工作列中
*/
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
	//#define UNREFERENCED_PARAMETER(P)          (P)
	//UNREFERENCED_PARAMETER(hPrevInstance)的意思就是告诉编译器，这个hPrevInstance参数我使用过了，别报警告了，仅此而已
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // TODO: 在此放置代码。

    // 初始化全局字符串
	/*
		LoadString就是从不同的资源里加载不同的字符串显示来实现的
		参数：
			hInstance是应用程序实例句柄。
			IDS_APP_TITLE是资源中的字符串编号。
			szTitle接收从资源里拷贝字符串出来的缓冲区。
			MAX_LOADSTRING指明缓冲的大小。
	*/
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_WINDOWSPROJECT, szWindowClass, MAX_LOADSTRING);

	/*
		MyRegisterClass 函数用于注册该窗口应用程序
		参数：HINSTANCE hInstance 窗口句柄
	*/
    MyRegisterClass(hInstance);

    // 执行应用程序初始化: 
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }

	/*
		LoadAccelerators函数功能：调入加速键表
		HACCEL LoadAccelerators（HINSTANCE hlnstance，LPCTSTR lpTableName）
		参数：
			hlnstance:模块的一个实例的句柄，该模块的可执行文件中包含将要调入的加速键表
			IpTableName:指向一个以空结尾的字符串的指针，该字符串包含了即将调入的加速键表的名字。另一种可选的方案是，
		该参数可以在加速键表资源的低位字中指定资源标识符，而高位字中全零。MAKEINTRESOURCE宏可被用于创建该值

		返回值：若函数调用成功，则返回所加载的加速键表句柄 。若函数调用失败，则返回值为NULL
	*/
    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WINDOWSPROJECT));

    MSG msg;

    // 主消息循环: 
	/*
		GetMessage是从调用线程的消息队列里取得一个消息并将其放于指定的结构。此函数可取得与指定窗口联系的消息和由PostThreadMessage寄送的线程消息。
		此函数接收一定范围的消息值。GetMessage不接收属于其他线程或应用程序的消息。
		获取消息成功后，线程将从消息队列中删除该消息。函数会一直等待直到有消息到来才有返回值

		参数：
			lpMsg：指向MSG结构的指针，该结构从线程的消息队列里接收消息信息。
			hWnd：取得其消息的窗口的句柄。当其值取NULL时，GetMessage为任何属于调用线程的窗口检索消息，线程消息通过PostThreadMessage寄送给调用线程。
			wMsgFilterMin：指定被检索的最小消息值的整数。
			wMsgFilterMax：指定被检索的最大消息值的整数。

		返回值：如果函数取得WM_QUIT之外的其他消息，返回非零值。如果函数取得WM_QUIT消息，返回值是零。如果出现了错误，返回值是-1
	*/
    while (GetMessage(&msg, nullptr, 0, 0))
    {
		/*
			TranslateAccelerator，函数功能：翻译加速键表。该函数处理菜单命令中的加速键。
			该函数将一个WM_KEYDOWN或WM_SYSKEYDOWN消息翻译成一个WM_COMMAND或WM_SYSCOMMAND消息（如果在给定的加速键表中有该键的入口），
			然后将WM_COMMAND或WM_SYSCOMMAND消息直接送到相应的窗口处理过程

			参数：
				hWnd:窗口句柄，该窗口的消息将被翻译。
				hAccTable:加速键表句柄。加速键表必须由LoadAccelerators函数调用装入或由CreateAccd_eratorTable函数调用创建。
				LpMsg:MSG结构指针，MSG结构中包含了从使用GetMessage或PeekMessage函数调用线程消息队列中得到的消息内容。

			返回值：若函数调用成功，则返回非零值；若函数调用失败，则返回值为零
		*/
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
			/*
				函数功能：该函数将虚拟键消息转换为字符消息。字符消息被寄送到调用线程的消息队列里，当下一次线程调用函数GetMessage或PeekMessage时被读出。
			
				参数：msg：指向含有消息的MSG结构的指针，该结构里含有用函数GetMessage或PeekMessage从调用线程的消息队列里取得的消息信息。
				
				返回值：如果消息被转换（即，字符消息被寄送到调用线程的消息队列里），返回非零值。
				如果消息是WM_KEYDOWN，WM_KEYUP WM_SYSKEYDOWN或WM_SYSKEYUP，返回非零值，不考虑转换。
				如果消息没被转换（即，字符消息没被寄送到调用线程的消息队列里），返回值是零。
				
				备注：此函数不修改由参数msg指向的消息
			*/
            TranslateMessage(&msg);
			
			/*
				函数功能：该函数分发一个消息给窗口程序。通常消息从GetMessage函数获得或者TranslateMessage函数传递的。消息被分发到回调函数（过程函数)，
				作用是消息传递给操作系统，然后操作系统去调用我们的回调函数，也就是说我们在窗体的过程函数中处理消息

				参数：msg：指向含有消息的MSG结构的指针。

				返回值：返回值是窗口程序返回的值。尽管返回值的含义依赖于被调度的消息，但返回值通常被忽略。
				
				备注：MSG结构必须包含有效的消息值。如果参数lpmsg指向一个WM_TIMER消息，并且WM_TIMER消息的参数IParam不为NULL，
				则调用IParam指向的函数，而不是调用窗口程序。
			*/
            DispatchMessage(&msg);
        }
    }

    return (int) msg.wParam;
}



//
//  函数: MyRegisterClass()
//
//  目的: 注册窗口类。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex;

    wcex.cbSize = sizeof(WNDCLASSEX); // 结构体WNDCLASSEX的大小

    wcex.style          = CS_HREDRAW | CS_VREDRAW;// 定义窗体样式
    wcex.lpfnWndProc    = WndProc; //回调函数
    wcex.cbClsExtra     = 0;// 窗口类额外字节数，通常为0
    wcex.cbWndExtra     = 0;// 窗口实例额外字节数，通常为0
    wcex.hInstance      = hInstance; //进程句柄
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WINDOWSPROJECT)); //图标
    wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);//鼠标样式
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1); //背景
    wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_WINDOWSPROJECT);//菜单指针
    wcex.lpszClassName  = szWindowClass;//窗体类名
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));// 窗口小图标

    return RegisterClassExW(&wcex);
}

//
//   函数: InitInstance(HINSTANCE, int)
//
//   目的: 保存实例句柄并创建主窗口
//
//   注释: 
//
//        在此函数中，我们在全局变量中保存实例句柄并
//        创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance; // 将实例句柄存储在全局变量中

   //创建主窗口
   HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);

   if (!hWnd)
   {
      return FALSE;
   }

   /*
		函数功能：该函数设置指定窗口的显示状态。
		参数：
			hWnd:指窗口句柄。
			nCmdShow：指定窗口如何显示
   */
   ShowWindow(hWnd, nCmdShow);

   //UpdateWindow，是计算机用语。意思是更新指定窗口的客户区。
   UpdateWindow(hWnd);

   return TRUE;
}

//
//  函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  目的:    处理主窗口的消息。
//
//  WM_COMMAND  - 处理应用程序菜单
//  WM_PAINT    - 绘制主窗口
//  WM_DESTROY  - 发送退出消息并返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);
            // 分析菜单选择: 
            switch (wmId)
            {
            case IDM_ABOUT:
				/*
					DialogBox作用是从一个对话框资源中创建一个模态对话框。
					该函数直到指定的回调函数通过调用EndDialog函数中止模态的对话框才能返回控制。该函数通过调用DialogBoxParam函数来实现

					参数：
						hlnstance 标识一个模块的事例该模块的可执行文件含有对话框模板。
						
						IpTemplate 标识对话框模板。此参数可以是指向一个以NULL结尾的字符串的指针，该字符串指定对话框模板名，
						或是指定对话框模板的资源标识符中的一个整型值。如果此参数指定了一个资源标识符则它的高位字一定为零，
						且低位字一定含有标识符。一定用MAKEINTRESOURCE宏指令创建此值。
						
						hWndParent 指定拥有对话框的窗口。
						
						IpDialogFunc 指向对话框过程的指针

					如果函数调用成功，则返回值为在对函数EndDialog的调用中的nResult参数．该函数用于中止对话框。如果函数调用失败，则返回值为-1
				*/
                DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                break;
            case IDM_EXIT:

				/*
					函数功能：销毁指定的窗口。这个函数通过发送WM_DESTROY 消息和 WM_NCDESTROY 消息使窗口无效并移除其键盘焦点。
					这个函数还销毁窗口的菜单，清空线程的消息队列，销毁与窗口过程相关的定时器，解除窗口对剪贴板的拥有权，打断剪贴板器的查看链

					hWnd ：将被销毁的窗口的句柄。

					返回值：如果函数成功，返回值为非零：如果函数失败，返回值为零
				*/
                DestroyWindow(hWnd);
                break;
            default:

				/*
					DefWindowProc函数调用缺省的窗口过程来为应用程序没有处理的任何窗口消息提供缺省的处理。该函数确保每一个消息得到处理

					参数：
						hWnd：指向接收消息的窗口过程的句柄。
						Msg：指定消息类型。
						wParam：指定其余的、消息特定的信息。该参数的内容与Msg参数值有关。
						IParam：指定其余的、消息特定的信息。该参数的内容与Msg参数值有关。
					
					返回值：返回值就是消息处理结果，它与发送的消息有关
				*/
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
        }
        break;
    case WM_PAINT:
        {

			//PAINTSTRUCT 结构体包含了用于绘制窗口客户区的信息
            PAINTSTRUCT ps;

			/*
				BeginPaint函数为指定窗口进行绘图工作的准备，并用将和绘图有关的信息填充到一个PAINTSTRUCT结构中

				参数：
					hwnd:[输入]被重绘的窗口句柄
					lpPaint:[输出]指向一个用来接收绘画信息的PAINTSTRUCT结构

				返回值：
					如果函数成功，返回值是指定窗口的“显示设备描述表”句柄。
					如果函数失败，返回值是NULL，表明没有得到显示设备的内容。
			*/
            HDC hdc = BeginPaint(hWnd, &ps);
            // TODO: 在此处添加使用 hdc 的任何绘图代码...

			/*
				EndPaint函数标记指定窗口的绘画过程结束

				参数：
					hWnd：[输入]已经被重画的窗口的HANDLE
					lpPaint：[输入]指向一个PAINTSTRUCT结构，该结构包含了绘画信息，是BeginPaint函数返回的返回值：
				
				返回值：返回值始终是非0
			*/
            EndPaint(hWnd, &ps);
        }
        break;
    case WM_DESTROY:

		//PostQuitMessage寄送一个WM_QUIT消息给线程的消息队列并立即返回；此函数向系统表明有个线程请求在随后的某一时间终止。
        PostQuitMessage(0);
        break;
    default:

		//
		/*
			DefWindowProc函数调用缺省的窗口过程来为应用程序没有处理的任何窗口消息提供缺省的处理。该函数确保每一个消息得到处理

			参数：
				hWnd：指向接收消息的窗口过程的句柄。
				Msg：指定消息类型。
				wParam：指定其余的、消息特定的信息。该参数的内容与Msg参数值有关。
				IParam：指定其余的、消息特定的信息。该参数的内容与Msg参数值有关。
			
			返回值：返回值就是消息处理结果，它与发送的消息有关。
		*/
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

// “关于”框的消息处理程序。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
			/*
				EndDialog，是清除一个模态对话框,并使系统中止对对话框的任何处理的函数。

				参数：
					hDlg：表示要被清除的对话框窗口。
					NResult：指定从创建对话框函数返回到应用程序的值。

				返回值：如果函数调用成功，则返回值为非零值；如果函数调用失败则返回值为零。若想获得错误信息请调用GetLastError函数。
			*/
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}
